//
//  UIButton+Extension.swift
//  YangFoundation
//
//  Created by yuyang on 2021/12/21.
//

import Foundation

public enum ImagePosition {
    case left
    case right
    case top
    case bottom
}

public extension UIButton {

    /// adjust button's imageView position to left/top/right/bottom relative to titleLabel with a spacing
    ///
    /// - Parameters:
    ///   - position: the imageView's new position
    ///   - spacing: the space between imageView and titleLabel
    func setImagePosition(_ position: ImagePosition, spacing: CGFloat) {
        setTitle(currentTitle, for: .normal)
        setImage(currentImage, for: .normal)

        imageEdgeInsets = .zero
        titleEdgeInsets = .zero
        contentEdgeInsets = .zero

        guard let label = titleLabel else {
            return
        }

        let imageWidth = imageView?.image?.size.width ?? 0
        let imageHeight = imageView?.image?.size.height ?? 0

        let tempLabel = UILabel()
        tempLabel.attributedText = label.attributedText
        tempLabel.sizeToFit()
        let labelSize = tempLabel.sizeThatFits(CGSize.init(width: 999, height: 999))

        let labelWidth = labelSize.width
        let labelHeight = labelSize.height

        let imageOffsetX = (imageWidth + labelWidth) / 2 - imageWidth / 2
        let imageOffsetY = imageHeight / 2 + spacing / 2
        let labelOffsetX = (imageWidth + labelWidth / 2) - (imageWidth + labelWidth) / 2
        let labelOffsetY = labelHeight / 2 + spacing / 2

        let tempWidth = Swift.max(labelWidth, imageWidth)
        let changedWidth = labelWidth + imageWidth - tempWidth
        let tempHeight = Swift.max(labelHeight, imageHeight)
        let changedHeight = labelHeight + imageHeight + spacing - tempHeight

        switch position {
        case .left:
            imageEdgeInsets = UIEdgeInsets(top: 0, left: -spacing / 2, bottom: 0, right: spacing / 2)
            titleEdgeInsets = UIEdgeInsets(top: 0, left: spacing / 2, bottom: 0, right: -spacing / 2)
            contentEdgeInsets = UIEdgeInsets(top: 0, left: -spacing / 2, bottom: 0, right: -spacing / 2)
        case .right:
            imageEdgeInsets = UIEdgeInsets(top: 0, left: labelWidth + spacing / 2, bottom: 0, right: -(labelWidth + spacing / 2))
            titleEdgeInsets = UIEdgeInsets(top: 0, left: -(imageWidth + spacing / 2), bottom: 0, right: imageWidth + spacing / 2)
            contentEdgeInsets = UIEdgeInsets(top: 0, left: spacing / 2, bottom: 0, right: spacing / 2)
        case .top:
            imageEdgeInsets = UIEdgeInsets(top: -imageOffsetY, left: imageOffsetX, bottom: imageOffsetY, right: -imageOffsetX)
            titleEdgeInsets = UIEdgeInsets(top: labelOffsetY, left: -labelOffsetX, bottom: -labelOffsetY, right: labelOffsetX)
            contentEdgeInsets = UIEdgeInsets(top: imageOffsetY, left: -changedWidth / 2, bottom: changedHeight - imageOffsetY, right: -changedWidth / 2)
        case .bottom:
            imageEdgeInsets = UIEdgeInsets(top: imageOffsetY, left: imageOffsetX, bottom: -imageOffsetY, right: -imageOffsetX)
            titleEdgeInsets = UIEdgeInsets(top: -labelOffsetY, left: -labelOffsetX, bottom: labelOffsetY, right: labelOffsetX)
            contentEdgeInsets = UIEdgeInsets(top: changedHeight - imageOffsetY, left: -changedWidth / 2, bottom: imageOffsetY, right: -changedWidth / 2)
        }
    }
}

// MARK: - 扩大点击区域

func associatedObject<ValueType: AnyObject>( base: AnyObject,
                                             key: UnsafePointer<UInt8>,
                                             initialiser: () -> ValueType ) -> ValueType {
    if let associated = objc_getAssociatedObject(base, key)
        as? ValueType { return associated }
    let associated = initialiser()
    objc_setAssociatedObject(base, key, associated, .OBJC_ASSOCIATION_RETAIN)
    return associated
}

func associateObject<ValueType: AnyObject>( base: AnyObject,
                                            key: UnsafePointer<UInt8>,
                                            value: ValueType) {
    objc_setAssociatedObject(base, key, value, .OBJC_ASSOCIATION_RETAIN)
}

private var topKey: UInt8 = 0
private var bottomKey: UInt8 = 0
private var leftKey: UInt8 = 0
private var rightKey: UInt8 = 0

extension UIButton {

    open func setEnlargeEdge(top: Float, bottom: Float, left: Float, right: Float) {
        self.btTop = NSNumber.init(value: top)
        self.btBottom = NSNumber.init(value: bottom)
        self.btLeft = NSNumber.init(value: left)
        self.btRight = NSNumber.init(value: right)
    }

    func enlargedRect() -> CGRect {
        let top = self.btTop
        let bottom = self.btBottom
        let left = self.btLeft
        let right = self.btRight
        if top.floatValue >= 0, bottom.floatValue >= 0, left.floatValue >= 0, right.floatValue >= 0 {
            return CGRect.init(x: self.bounds.origin.x - CGFloat(left.floatValue),
                               y: self.bounds.origin.y - CGFloat(top.floatValue),
                               width: self.bounds.size.width + CGFloat(left.floatValue) + CGFloat(right.floatValue),
                               height: self.bounds.size.height + CGFloat(top.floatValue) + CGFloat(bottom.floatValue))
        } else {
            return self.bounds
        }
    }

    var btTop: NSNumber {
        get {
            return associatedObject(base: self, key: &topKey) { return 0 }
        }
        set {
            associateObject(base: self, key: &topKey, value: newValue)
        }
    }

    var btBottom: NSNumber {
        get {
            return associatedObject(base: self, key: &bottomKey) { return 0 }
        }
        set {
            associateObject(base: self, key: &bottomKey, value: newValue)
        }
    }

    var btLeft: NSNumber {
        get {
            return associatedObject(base: self, key: &leftKey) { return 0 }
        }
        set {
            associateObject(base: self, key: &leftKey, value: newValue)
        }
    }

    var btRight: NSNumber {
        get {
            return associatedObject(base: self, key: &rightKey) { return 0 }
        }
        set {
            associateObject(base: self, key: &rightKey, value: newValue)
        }
    }

    open override func point(inside point: CGPoint, with event: UIEvent?) -> Bool {
        let rect = self.enlargedRect()
        if rect.equalTo(self.bounds) {
            return super.point(inside: point, with: event)
        }
        return rect.contains(point) ? true : false
    }

}
